<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Advanced Controls in Mono Winforms</title>
<link rel="stylesheet" href="/cfg/format.css" type="text/css">
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="keywords" content="Winforms, Mono, C#, advanced controls, programming, tutorial">
<meta name="description" content="Advanced Controls">
<meta name="language" content="en">
<meta name="author" content="Jan Bodnar">
<meta name="distribution" content="global">

<script type="text/javascript" src="/lib/jquery.js"></script>
<script type="text/javascript" src="/lib/common.js"></script>

</head>

<body>

<div class="container">

<div id="wide_ad" class="ltow">
<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* 160x600, August 2011 */
google_ad_slot = "2484182563";
google_ad_width = 160;
google_ad_height = 600;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>

<div class="content">


<a href="/" title="Home">Home</a>&nbsp;
<a href=".." title="Home">Contents</a>


<h1>Advanced Controls in Mono Winforms</h1>


<p>
In this part of the Mono Winforms tutorial, we introduce some more 
advanced controls. Namely the <b class="keyword">ListBox</b>, the 
<b class="keyword">ListView</b> and the <b class="keyword">TreeView</b> control.
</p>

<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* NewSquare */
google_ad_slot = "0364418177";
google_ad_width = 300;
google_ad_height = 250;
//-->
</script> 
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> 
</script> 


<h2>ListBox Control</h2>

<p>
The <b class="keyword">ListBox</b> control is used to display a list of items. 
Users can select one or more
items by clicking on them. 
</p>

<div class="codehead">listbox.cs</div>
<pre class="code">
using System;
using System.Drawing;
using System.Windows.Forms;

class MForm : Form {

    private StatusBar sb;

    public MForm() {
        Text = "ListBox";
        Size = new Size(210, 210);

        ListBox lb = new ListBox();
        lb.Parent = this;
        lb.Items.Add("Jessica");
        lb.Items.Add("Rachel");
        lb.Items.Add("Angelina");
        lb.Items.Add("Amy");
        lb.Items.Add("Jennifer");
        lb.Items.Add("Scarlett");

        lb.Dock = DockStyle.Fill;
        lb.SelectedIndexChanged += new EventHandler(OnChanged);

        sb = new StatusBar();
        sb.Parent = this;

        CenterToScreen();
    }


    void OnChanged(object sender, EventArgs e) {
        ListBox lb = (ListBox) sender;
        sb.Text = lb.SelectedItem.ToString();
    }
}

class MApplication {
    public static void Main() {
        Application.Run(new MForm());
    }
}
</pre>

<p>
Our example shows a listbox with six names. The selected item is shown in the statusbar. 
</p>

<pre class="explanation">
 ListBox lb = new ListBox();
 lb.Parent = this;
</pre>

<p>
<b class="keyword">ListBox</b> control is created. 
</p>

<pre class="explanation">
 lb.Items.Add("Jessica");
</pre>

<p>
This is how we add a new item to the <b class="keyword">ListBox</b> control. 
The control has the <b class="keyword">Items</b>
property. The property is a reference to the list of items in a listbox. 
Using this reference, we can add, remove or get count
of items of the listbox. 
</p>

<pre class="explanation">
 lb.SelectedIndexChanged += new EventHandler(OnChanged);
</pre>

<p>
<b class="keyword">SelectedIndexChanged</b> event is triggered, when we select an item. 
</p>

<pre class="explanation">
 ListBox lb = (ListBox) sender;
 sb.Text = lb.SelectedItem.ToString();
</pre>

<p>
Inside the <b class="keyword">OnChange()</b> method, we get the reference 
to the listbox and set 
the selected text to the statusbar.
</p>


<br>
<img src="/img/gui/sharpwinf/listbox.png" alt="ListBox">
<div class="figure">Figure: ListBox</div>


<h2>ListView</h2>

<p>
<b class="keyword">ListView</b> control is used to display collections of items. 
It is a more sophisticated control
than the <b class="keyword">ListBox</b> control. It can display data in various 
views is mostly used to display data in multicolumn views. 
</p>

<div class="codehead">listview.cs</div>
<pre class="code">
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Collections.Generic;


public class Actress
{
    public string name;
    public int year;
 
    public Actress(string name, int year)
    {
        this.name = name;
        this.year = year;
    }
}

class MForm : Form {

    private StatusBar sb;

    public MForm() {
        Text = "ListView";
        Size = new Size(350, 300);


        List&lt;Actress&gt; actresses = new List&lt;Actress&gt;();

        actresses.Add(new Actress("Jessica Alba", 1981));
        actresses.Add(new Actress("Angelina Jolie", 1975));
        actresses.Add(new Actress("Natalie Portman", 1981));
        actresses.Add(new Actress("Rachel Weiss", 1971));
        actresses.Add(new Actress("Scarlett Johansson", 1984));


        ColumnHeader name = new ColumnHeader();
        name.Text = "Name";
        name.Width = -1;
        ColumnHeader year = new ColumnHeader();
        year.Text = "Year";

        SuspendLayout();

        ListView lv = new ListView();
        lv.Parent = this;
        lv.FullRowSelect = true;
        lv.GridLines = true;
        lv.AllowColumnReorder = true;
        lv.Sorting = SortOrder.Ascending;
        lv.Columns.AddRange(new ColumnHeader[] {name, year});
        lv.ColumnClick += new ColumnClickEventHandler(ColumnClick);

        foreach (Actress act in actresses) {
            ListViewItem item = new ListViewItem();
            item.Text = act.name;
            item.SubItems.Add(act.year.ToString());
            lv.Items.Add(item);
        }

        lv.Dock = DockStyle.Fill;
        lv.Click += new EventHandler(OnChanged);

        sb = new StatusBar();
        sb.Parent = this;
        lv.View = View.Details;

        ResumeLayout();

        CenterToScreen();
    }

    void OnChanged(object sender, EventArgs e) {
        ListView lv = (ListView) sender;
        string name = lv.SelectedItems[0].SubItems[0].Text;
        string born = lv.SelectedItems[0].SubItems[1].Text;
        sb.Text = name + ", " + born;
    }

    void ColumnClick(object sender, ColumnClickEventArgs e)
    {
        ListView lv = (ListView) sender;

        if (lv.Sorting == SortOrder.Ascending) {
            lv.Sorting = SortOrder.Descending;
        } else {
            lv.Sorting = SortOrder.Ascending;
        }   
    }
}

class MApplication {
    public static void Main() {
        Application.Run(new MForm());
    }
}
</pre>

<p>
In our example, we have a listview with two columns. In the first column, we 
display the name of the actress. In the second one their date of birth. 
The data is store in a <b class="keyword">List</b> collection. By selecting a row, the
data in a row is displayed in the statusbar. Also, by clicking on the 
column header, the data is sorted. 
</p>

<pre class="explanation">
 public class Actress
 {
 ...
 }
</pre>

<p>
We use the <b class="keyword">Actress</b> class to store our data. 
</p>

<pre class="explanation">
 List&lt;Actress&gt; actresses = new List&lt;Actress&gt;();

 actresses.Add(new Actress("Jessica Alba", 1981));
 actresses.Add(new Actress("Angelina Jolie", 1975));
 ...
</pre>

<p>
We create and fill our collection with items. 
</p>

<pre class="explanation">
 ColumnHeader name = new ColumnHeader();
 name.Text = "Name";
 name.Width = -1;
</pre>

<p>
For each column in a listview, we create a <b class="keyword">ColumnHeader</b>. 
By setting the  <b class="keyword">Width</b>
to -1, the width of the column is equal to the longest item in the column. 
</p>

<pre class="explanation">
 ListView lv = new ListView();
 lv.Parent = this;
</pre>

<p>
<b class="keyword">ListView</b> control is created. 
</p>

<pre class="explanation">
 lv.FullRowSelect = true;
 lv.GridLines = true;
 lv.AllowColumnReorder = true; 
 lv.Sorting = SortOrder.Ascending;
</pre>

<p>
Here we set four properties of the control. This code lines enable full 
row selection, show grid lines, allow column reordering by dragging the 
columns and sort the data in ascending order.
</p>

<pre class="explanation">
 lv.Columns.AddRange(new ColumnHeader[] {name, year});
</pre>

<p>
Here we add two <b class="keyword">ColumnHeader</b>s to the 
<b class="keyword">ListView</b> control. 
</p>

<pre class="explanation">
 foreach (Actress act in actresses) {
     ListViewItem item = new ListViewItem();
     item.Text = act.name;
     item.SubItems.Add(act.year.ToString());
     lv.Items.Add(item);
 }
</pre>

<p>
This cycle populates the listview control. Each row is added to the 
listview as a <b class="keyword">ListViewItem</b> class.
</p>


<pre class="explanation">
 lv.View = View.Details;
</pre>

<p>
The <b class="keyword">ListView</b> control can have different views. 
Different views display data differently.
</p>


<pre class="explanation">
 ListView lv = (ListView) sender;
 string name = lv.SelectedItems[0].SubItems[0].Text;
 string born = lv.SelectedItems[0].SubItems[1].Text;
 sb.Text = name + ", " + born;
</pre>

<p>
Inside the <b class="keyword">OnChanged()</b> method, we get the data 
from the selected row and show it on the statusbar. 
</p>


<pre class="explanation">
 if (lv.Sorting == SortOrder.Ascending) {
     lv.Sorting = SortOrder.Descending;
 } else {
     lv.Sorting = SortOrder.Ascending;
 }   
</pre>

<p>
Here we toggle the sorting order of the column. 
</p>

<br>
<img src="/img/gui/sharpwinf/listview.png" alt="ListView">
<div class="figure">Figure: ListView</div>


<h2>TreeView</h2>

<p>
<b class="keyword">TreeView</b> control displays hierarchical collection 
of items. Each item in this control is represented by a 
<b class="keyword">TreeNode</b> object. 
</p>

<div class="codehead">treeview.cs</div>
<pre class="code">
using System;
using System.Drawing;
using System.Windows.Forms;

class MForm : Form {

    StatusBar sb;

    public MForm() {
        Text = "TreeView";
        Size = new Size(250, 250);

        TreeView tv = new TreeView();

        TreeNode root = new TreeNode();
        root.Text = "Languages";

        TreeNode child1 = new TreeNode();
        child1.Text = "Python";

        TreeNode child2 = new TreeNode();
        child2.Text = "Ruby";

        TreeNode child3 = new TreeNode();
        child3.Text = "Java";

        root.Nodes.AddRange(new TreeNode[] {child1, child2, child3});

        tv.Parent = this;
        tv.Nodes.Add(root);
        tv.Dock = DockStyle.Fill;
        tv.AfterSelect += new TreeViewEventHandler(AfterSelect);

        sb = new StatusBar();
        sb.Parent = this;

        CenterToScreen();
    }

    void AfterSelect(object sender, TreeViewEventArgs e)
    {
        sb.Text = e.Node.Text;
    }
}

class MApplication {
    public static void Main() {
        Application.Run(new MForm());
    }
}
</pre>

<p>
This is a very simple demonstration of the <b class="keyword">TreeView</b> 
control. We have one root item and three children. 
</p>


<pre class="explanation">
 TreeView tv = new TreeView();
</pre>

<p>
We create the <b class="keyword">TreeView</b> control.
</p>

<pre class="explanation">
 TreeNode root = new TreeNode();
 root.Text = "Languages";
 ...
 tv.Nodes.Add(root);
</pre>

<p>
Here we create a root node. 
</p>

<pre class="explanation">
 TreeNode child1 = new TreeNode();
 child1.Text = "Python";
</pre>

<p>
Child node is created in a similar way. 
</p>

<pre class="explanation">
 root.Nodes.AddRange(new TreeNode[] {child1, child2, child3});
</pre>

<p>
Child nodes are plugged into the <b class="keyword">Nodes</b> 
property of the root node. 
</p>


<br>
<img src="/img/gui/sharpwinf/treeview.png" alt="TreeView">
<div class="figure">Figure: TreeView</div>


<h2>Directories</h2>

<p>
The following code example will examine the <b class="keyword">TreeView</b> control more
in-depth. 
</p>

<div class="codehead">directories.cs</div>
<pre class="code">
using System;
using System.Drawing;
using System.Windows.Forms;
using System.IO;       

public class MForm : Form
{
  private TreeView tv;
  private Button expand;
  private Button expandAll;
  private Button collapse;
  private Button collapseAll;
  private StatusBar sb;

  private const string HOME_DIR = "/home/vronskij";

  public MForm()
  {
    Size = new Size(400, 400);
    Text = "Directories";

    tv = new TreeView();

    SuspendLayout();

    tv.Parent = this;
    tv.Location = new Point(10,10);
    tv.Size = new Size(ClientSize.Width - 20, Height - 200);
    tv.Anchor = AnchorStyles.Top | AnchorStyles.Left | 
          AnchorStyles.Right ;


    tv.FullRowSelect = false;    
    tv.ShowLines = true;      
    tv.ShowPlusMinus = true;    
    tv.Scrollable = true;      
    tv.AfterSelect += new TreeViewEventHandler(AfterSelect);

    expand = new Button();
    expand.Parent = this;
    expand.Location = new Point(20, tv.Bottom + 20);
    expand.Text = "Expand";
    expand.Anchor = AnchorStyles.Left | AnchorStyles.Top;
    expand.Click += new EventHandler(OnExpand);

    expandAll = new Button();
    expandAll.Parent = this;
    expandAll.Location = new Point(20, expand.Bottom + 5);
    expandAll.Text = "Expand All";
    expandAll.Anchor = AnchorStyles.Left | AnchorStyles.Top;
    expandAll.Click += new EventHandler(OnExpandAll);

    collapse = new Button();
    collapse.Parent = this;
    collapse.Location = new Point(expandAll.Right + 5, expand.Top );
    collapse.Text = "Collapse";
    collapse.Anchor = AnchorStyles.Left | AnchorStyles.Top;
    collapse.Click += new EventHandler(OnCollapse);

    collapseAll = new Button();
    collapseAll.Parent = this;
    collapseAll.Location = new Point(collapse.Left, collapse.Bottom + 5);
    collapseAll.Text = "Collapse All";
    collapseAll.Anchor = AnchorStyles.Left | AnchorStyles.Top;
    collapseAll.Click += new EventHandler(OnCollapseAll);

    sb = new StatusBar();
    sb.Parent = this;


    ShowDirectories(tv.Nodes, HOME_DIR);

    ResumeLayout();

    CenterToScreen();
  }


  void AfterSelect(object sender, TreeViewEventArgs e)
  {
      sb.Text = e.Node.Text;
  }


  void ShowDirectories(TreeNodeCollection trvNode, string path)
  {
      DirectoryInfo dirInfo = new DirectoryInfo(path);
      if (dirInfo != null)
      {
          DirectoryInfo[] subDirs = dirInfo.GetDirectories();
          TreeNode tr = new TreeNode(dirInfo.Name);

          if (subDirs.Length > 0)
          {
              foreach (DirectoryInfo dr in subDirs)
              {   
                  if (!dr.Name.StartsWith("."))
                      ShowDirectories(tr.Nodes, dr.FullName);               
              }
          }
          trvNode.Add(tr);
      }
  }


  void OnExpand(object sender, EventArgs e)
  {
    tv.SelectedNode.Expand();
  }

  void OnExpandAll(object sender, EventArgs e)
  {
    tv.ExpandAll();
  }

  void OnCollapse(object sender, EventArgs e)
  {
    tv.SelectedNode.Collapse();
  }

  void OnCollapseAll(object sender, EventArgs e)
  {
    tv.CollapseAll();
  }

  static void Main() 
  {
    Application.Run(new MForm());
  }

}
</pre>

<p>
Our code example shows the directories of the specified home directory in 
a <b class="keyword">TreeView</b> control. The application starts with some delay,
because it reads the directory structure of the home directory first. 
We have also four buttons on the form. The buttons expand and collapse nodes
programatically. 
</p>


<pre class="explanation">
 tv.Scrollable = true; 
</pre>

<p>
We make the treeview control scrollable, because the control shows lots of 
directories. 
</p>


<pre class="explanation">
 ShowDirectories(tv.Nodes, HOME_DIR);
</pre>

<p>
The <b class="keyword">ShowDirectories()</b> method fills the nodes of the
treview control with directories available in the specified home directory. 
</p>

<pre class="explanation">
 if (subDirs.Length > 0)
 {
 ...
 }
</pre>

<p>
We check if there are any subdirectories. 
</p>

<pre class="explanation">
 foreach (DirectoryInfo dr in subDirs)
 {   
     if (!dr.Name.StartsWith("."))
         ShowDirectories(tr.Nodes, dr.FullName);               
 }
</pre>

<p>
We loop through all directories. For this, we use the recursion algorithm.
We also skip the hidden directories. They begin with a dot on Unix systems. 
</p>

<pre class="explanation">
 trvNode.Add(tr);
</pre>

<p>
This code line actually adds the directory to the treeview control. 
</p>

<pre class="explanation">
  void OnExpand(object sender, EventArgs e)
  {
    tv.SelectedNode.Expand();
  }
</pre>

<p>
All four buttons have events plugged to a method. Here is a method for the Expand button.
It calls the <b class="keyword">Expand()</b> method of the currently selected node.
</p>

<br>
<img src="/img/gui/sharpwinf/directories.png" alt="Directories">
<div class="figure">Figure: Directories</div>

<hr class="btm">

<p>
In this part of the Mono Winforms tutorial, we covered several advanced controls
available in Winforms library.
</p>



<div class="botNav, center">
<span class="botNavItem"><a href="/">Home</a></span> ‡ <span class="botNavItem"><a href="..">Contents</a></span> ‡ 
<span class="botNavItem"><a href="#">Top of Page</a></span>
</div>


<div class="footer">
<div class="signature">
<a href="/">ZetCode</a> last modified December 28, 2008  <span class="copyright">&copy; 2007 - 2012 Jan Bodnar</span>
</div>
</div>

</div> <!-- content -->

</div> <!-- container -->

</body>
</html>

